//
//  transformData.m
/*
 Copyright 2009 Jack Whitsitt
 Conact: sintixerr@gmail.com | http://sintixerr.wordpress.com
 
 This file is part of "pkviz".
 
 "pkviz" is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 "pkviz" is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with "pkviz".  If not, see <http://www.gnu.org/licenses/>.
 */ 

#import "transformData.h"


@implementation transformData

@synthesize theView;
@synthesize manualPacketNum;
@synthesize demoSpeed;
@synthesize packetSetProgress;
@synthesize playpauseButton;
@synthesize packetTextWindowVisible;
@synthesize pkText;
@synthesize expandedHeaderButtonEnabled;
@synthesize headersExpanded;


// *********************** INIT *****************************
// **********************************************************

-(id) init 
{
    self = [super init];
    ranOnce = 0;
	[self setExpandedHeaderButtonEnabled:[NSNumber numberWithInt:1]];
    return self;
}

// **********************************************************
// **********************************************************




// ************* PRIMARY ACTIONS AND METHODS ******************
// **********************************************************


- (IBAction)transData:(id)sender // this is the main action kicking off animation. maps to "render" button
{
	
	[self setDemoSpeed:[NSNumber numberWithFloat:2.5]]; // default speed. Not sure why i called it demospeed :)
	
	//**********SET UP*******
	
	keypacket = 0;
	currentDirection = FORWARD;
	
	[self formatPackets]; // pull data from Core Data store into an array
	
	[theView setWindowSize:30];
	[theView setupLayers];
	
	iter = 0;
	
	pauseDisplay = 0;
	
	[theView setHeadersExpanded:[self headersExpanded]]; 
	[self setExpandedHeaderButtonEnabled:[NSNumber numberWithInt:0]];//this button needs to be disabled while rendering
	
	[NSThread detachNewThreadSelector:@selector(packetEnumThread:) toTarget:self withObject:bytepakposSet]; // start enuming packets to be displayed
	
	
}



- (void)packetEnumThread:(NSMutableArray *)pkset // Animation Loop Thread
// runs in a different thread than main. loops through packets and pops them out one at a time
// we do this because if we loop through the packets in the main thread, 
// NSView/Core Animation will wait until we're done looping to update display

{

	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
		
	int tmp_kp = 0;
	int firstread = 0;
	
	while (tmp_kp <= ([bytepakposSet count])-1) 
	{
		if (firstread !=0 && (tmp_kp != ([bytepakposSet count]-1)))
		{
			++tmp_kp;
		}
		else {
			++firstread;
		}

		if (tmp_kp == ([bytepakposSet count] - 1))
		{
			[self setPause];
		}
				
		while (pauseDisplay == 1) 
		{
			tmp_kp = keypacket;
		}
		
		//we can only return a single object from the thread to main, so we add the current packet and current packet# to one array and return it
		NSArray *pk_and_kp = [NSArray arrayWithObjects:[bytepakposSet objectAtIndex:tmp_kp],[NSNumber numberWithInt:tmp_kp],nil];
		[self performSelectorOnMainThread:@selector(packetEnumReceiver:) withObject:pk_and_kp waitUntilDone:NO];

		// we also drive the progress bar from here
		float progress_kp = tmp_kp;
		float progress = (100*progress_kp)/[bytepakposSet count];
		
		[self setPacketSetProgress:[NSNumber numberWithFloat:progress]];
		
		//this is how we control animation speed. if we dont sleep a little, we will skip displaying some packets. dont want that.
		[NSThread sleepForTimeInterval:(.1/[[self demoSpeed] floatValue])];		

		
	}
//	[theView removeLayers];
	[pool release];
}



- (void)notifyPkGraphView:(NSArray *)aPacket inDirection:(int)dir // tell the view that we have stuff to draw now that we have new keypacket# 
{

	[testlabel setIntValue:keypacket]; // just a user interface label for current packet
 
	if (dir == FORWARD)
	{
		[theView moveOneFrameForward:aPacket withKp:keypacket];
	}
	else if (dir == BACK)
	{
		[theView moveOneFrameBack:aPacket withKp:keypacket];
	}
	else if (dir == JUMP) // jump goes to an arbitrary specific packet, not just next one forward or back
	{
		if (keypacket < last_kp) // are we jumping backward?
		{
			int kplast_diff = last_kp - keypacket;
			
			if (kplast_diff > 1)
			{
				int remloop = last_kp;
				
				while (remloop > keypacket) // loop through packets we've already drawn and remove them
				{
					[theView removeThisPacket:remloop];
					--remloop;
				}
				currentDirection = BACK; // probably redundant, but doing it for consistency
				[theView moveOneFrameBack:aPacket withKp:keypacket];
			}
			
		}
		
		else if (keypacket > last_kp) // are we jumping forward?
		{
			int kplast_diff = keypacket - last_kp;
			
			if (kplast_diff > 1)
			{
				int remloop = last_kp;
				
				while (remloop < (keypacket)) // loop through packets we've already drawn and remove them
				{
					[theView removeThisPacket:remloop];
					++remloop;
				}
				
				currentDirection = FORWARD; // probably redundant, but doing it for consistency
				[theView moveOneFrameForward:aPacket withKp:keypacket];
			}
			else 
			{

			}
			
		}
	
	}
	
}



- (void)formatPackets // get packets from core data store. arrays are better for speed, but we're doing core data to prepare for future use cases
{
	
	NSManagedObjectContext *mycontext = [[NSApp delegate] managedObjectContext];
	
	
	NSFetchRequest *request = [[NSFetchRequest alloc] init];
	[request setEntity:[NSEntityDescription entityForName:@"packet" inManagedObjectContext:mycontext]];
	
	NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"packetNum" ascending:YES];
	
	[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
	
	[sortDescriptor release];
	
	NSError *error = nil;
	
	NSArray *all_paks = [mycontext executeFetchRequest:request error:&error];
	
	[request release];
	
	int packetloop = 0;
	
	bytepakposSet = [NSMutableArray arrayWithCapacity:100000];
	
	while (packetloop < [all_paks count]) 
	{ // looping through all packets 
		
		
		NSArray *packetBytes = [[[all_paks objectAtIndex:packetloop] valueForKey:@"bytes"] allObjects]; // putting bytes from current packet in array
		
		int byteloop = 0;
		
		NSMutableArray *bytesInPacket = [NSMutableArray arrayWithCapacity:[packetBytes count]];
		
		while (byteloop < [packetBytes count]) // getting each individual byte value from array and doing something with it
		{
			NSString *tmpBval = [[packetBytes objectAtIndex:byteloop] valueForKey:@"byteValue"];
			
			NSNumber *bytehexint = [NSNumber numberWithInt:[self hexStringToInt:tmpBval]];
			NSNumber *pos = [NSNumber numberWithInt:[[[packetBytes objectAtIndex:byteloop] valueForKey:@"position"] intValue]];
			
			NSMutableArray *bytePos = [NSMutableArray arrayWithObjects:bytehexint,pos,nil]; //byte+pos pair array
			[bytesInPacket addObject:bytePos];
			
			++byteloop;
		}
		
		
		[bytepakposSet addObject:bytesInPacket];
		
		++packetloop;
		
	}
	all_paks = nil;
	
	mycontext = nil;
	
	sortDescriptor = nil;
	
}

// **********************************************************
// **********************************************************




// **************ADDITIONAL USER CONTROLS********************
// **********************************************************


- (IBAction)showPacketText:(id)sender //unhides packet text window and displays ascii-hex of current packet
{
	
	[self setPacketTextWindowVisible:[NSNumber numberWithInt:1]];
	
	//the bytes in our packets arent inherently in positional order, but we need them to be to print them out
	
	NSMutableArray *packetToPrint = [NSMutableArray arrayWithCapacity:1600];
	
	int fillarray = 0;
	
	while (fillarray < 1600)
	{
		[packetToPrint addObject:[NSNumber numberWithInt:99999]]; // using 99999 as filler since we'll never see 99999 in any specific position
		++fillarray;
	}
	
	int current_byte = 0;
	
	int maxpos = 0;
	
	//NSArray *display_packet = [bytepakposSet objectAtIndex:keypacket];
	
	
	while (current_byte < [[bytepakposSet objectAtIndex:keypacket] count]) // grab byte and byte position array from bytepakposSet...use to place in new array
		//but in right order 
	{
		
		[packetToPrint replaceObjectAtIndex:[[[[bytepakposSet objectAtIndex:keypacket] objectAtIndex:current_byte] objectAtIndex:1] intValue] withObject:[[[bytepakposSet objectAtIndex:keypacket] objectAtIndex:current_byte] objectAtIndex:0]]; 
		
		if ([[[[bytepakposSet objectAtIndex:keypacket] objectAtIndex:current_byte] objectAtIndex:1] intValue] > maxpos)
		{
			maxpos = [[[[bytepakposSet objectAtIndex:keypacket] objectAtIndex:current_byte] objectAtIndex:1] intValue];
		}
		
		++current_byte;
	}
	
	int cb2 = 0;
	int col = 0;
	int row = 0;
	int pair = 0;
	//pkText = @"";
	
	
	NSMutableString *gah2 = [NSMutableString stringWithCapacity:3500];
	
	[gah2 appendFormat:@"Packet Number: %i \n---------------------------------------\n",keypacket];
	
	while (cb2 <= maxpos)
	{
		int gah_int = [[packetToPrint objectAtIndex:cb2] intValue];
		
		++pair;
		++col;
		
		[gah2 appendFormat: @"%@",[NSString stringWithFormat:@"%.2x",gah_int]];
		
		if (pair == 2)
		{
			[gah2 appendString:@" "];
			pair = 0;
		}
		
		if (col == 16)
		{
			[gah2 appendString:@"\n"];
			++row;
			col = 0;
		}
		
		++cb2;
	}
	
	[gah2 appendString:@"\n---------------------------------------"];
	
	[self setPkText:gah2];	
}



- (IBAction)hidePacketText:(id)sender
{
	[self setPacketTextWindowVisible:[NSNumber numberWithInt:0]];
}



- (IBAction)setPacketNum:(id)sender // action to jump to user-specified packet
{
	if ((pauseDisplay == 1) && (keypacket < [bytepakposSet count]))
	{
		int mpkn = [manualPacketNum intValue];
		
		if ((mpkn < [bytepakposSet count]) && (mpkn >= 0))
		{
			
			[self updateKeyPacket:mpkn];
			
			currentDirection = JUMP;
			
			[self notifyPkGraphView:[bytepakposSet objectAtIndex:keypacket] inDirection:currentDirection];
			
		}
	}
}



- (IBAction)incrementPacket:(id)sender
{
	if ((pauseDisplay == 1) && (keypacket < [bytepakposSet count]))
	{
		currentDirection = FORWARD;
		[self updateKeyPacket:(keypacket + 1)];
		[self notifyPkGraphView:[bytepakposSet objectAtIndex:keypacket] inDirection:currentDirection];
	}
}

- (IBAction)decrementPacket:(id)sender
{
	if ((pauseDisplay == 1) && (keypacket > 0)) 
	{
		[self updateKeyPacket: (keypacket - 1)];
		currentDirection = BACK;
		
		[self notifyPkGraphView:[bytepakposSet objectAtIndex:keypacket] inDirection:currentDirection];
		
		
	}
}



- (IBAction)haltDisplay:(id)sender //this is specifically the user-controled pause action
// the setpause and unset pause are separate functions because other code may pause animation

{
	if (pauseDisplay == 0) 
	{
		[self setPause];
	}
	else if (pauseDisplay == 1)
	{
		[self unSetPause];
	}
	
}

// **********************************************************
// **********************************************************




//**********************SUPPORTING***************************
// **********************************************************

- (void)packetEnumReceiver:(NSArray *)pkkp // this is the selector our packet enum thread calls when it has a new packet
{
	
	[self updateKeyPacket:[[pkkp objectAtIndex:1] intValue]]; // let everyone else know what we have a new current/keypacket. this is why most
	//things that require changing the keypacket need the user to pause the enum first. pkkp object at index 1 is the current keypacket#
	
	currentDirection = FORWARD; // if we're going to be driving the graph based on the automatic looping, let's make sure it's in the right direction :)
	[self notifyPkGraphView:[pkkp objectAtIndex:0] inDirection:currentDirection]; // pkkp object at index 0 is the current packet itself.
	
	
}



- (void)updateKeyPacket:(int)kp //this is a function just so everyone always remembers to set last_kp
{
	last_kp = keypacket;
	keypacket = kp;
}



- (void)setPause
{
	pauseDisplay = 1;
	
	NSImage *tmpplaypause = [playpauseButton image];
	[playpauseButton setImage:[playpauseButton alternateImage]];
	[playpauseButton setAlternateImage:tmpplaypause];
}



- (void)unSetPause
{
	if (currentDirection == BACK)
	{
		currentDirection = FORWARD;
	}
	
	pauseDisplay = 0;
	
	//[playpauseButton setState:NSOffState];
	
	NSImage *tmpplaypause2 = [playpauseButton image];
	[playpauseButton setImage:[playpauseButton alternateImage]];
	[playpauseButton setAlternateImage:tmpplaypause2];
}


- (int)hexStringToInt: (NSString *)hexString // for some reason, i store the hex values from tcpdump as strings, so have to convert back. 
{
	NSNumber *bval_num;
	
	NSScanner *scanner;
	int tempInt;
	
	NSString *hexVal = [@"0x" stringByAppendingString:hexString];
	
	scanner = [NSScanner scannerWithString:hexVal];
	[scanner scanHexInt:&tempInt];
	bval_num = [NSNumber numberWithInt:tempInt];
	
	int bval_int = [bval_num intValue];
	
	return bval_int;
}

// **********************************************************
// **********************************************************




// ************ UNUSED **************************************
// **********************************************************
- (void) drawPrep
{

/*	loop through packets, 
		look for filter criteria (like bytepos 12 sets the green) and set those atributes here
 
		loop through bytes in packets
			create pixels with: x, y, size, color, pos
			IF POSSIBLE create paths with this data, add paths to an array
			IF NOT POSSIBLE add pixel data to an array
*/
 }


@end
